package cn.ante.ymcc.service.impl;

import cn.ante.ymcc.domain.Course;
import cn.ante.ymcc.domain.CourseMarket;
import cn.ante.ymcc.domain.KillActivity;
import cn.ante.ymcc.domain.KillCourse;
import cn.ante.ymcc.dto.KillParamDto;
import cn.ante.ymcc.dto.PreCourseOrder;
import cn.ante.ymcc.mapper.KillCourseMapper;
import cn.ante.ymcc.service.IKillActivityService;
import cn.ante.ymcc.service.IKillCourseService;
import cn.ante.ymcc.util.AssertUtil;
import cn.ante.ymcc.util.CodeGenerateUtils;
import cn.ante.ymcc.vo.Course2OrderInfoVo;
import cn.ante.ymcc.vo.CourseDetailVo;
import cn.ante.ymcc.vo.CourseDto;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Nai
 * @since 2022-08-23
 */
@Service
@Slf4j
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillActivityService killActivityService;

    @Autowired
    private IKillCourseService killCourseService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
    * @Description: 将普通课程加入秒杀课程
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/23 21:04
    */
    @Override
    public void addKillCourse(KillCourse killCourse) {
        // 校验课程是否是上架状态 如果是下架子啊前端对按钮进行控制
        // 判断秒杀的课程没有再当前的活动中
        KillCourse killCourseTemp = checkKillCourse(killCourse.getCourseId(), killCourse.getActivityId());
        AssertUtil.isNull(killCourseTemp,"秒杀活动中已有该课程");
        // 判断秒杀课程的价格是否低于原课程的价格  要调用feign

        // 根据秒杀课程的中活动id查询活动是否存在
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        AssertUtil.isNotNull(killActivity,"活动不存在");
        // 活动状态是否是未发布
        boolean isWillPublish = killActivity.getPublishStatus() == KillActivity.STATES_WAIT_PUBLISH;
        AssertUtil.isTrue(isWillPublish,"活动已经开始");

        // 保存秒杀课程
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setStartTime(killActivity.getBeginTime());
        killCourse.setKillLimit(1);
        killCourse.setPublishStatus(killActivity.getPublishStatus());
        killCourse.setCreateTime(new Date());
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setPublishTime(killActivity.getPublishTime());
        insert(killCourse);
    }

    /**
    * @Description: 首页展示秒杀课程
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/25 12:53
    */
    @Override
    public List<KillCourse> indexKillcourse() {
        // 拿到所有的秒杀活动
        Set<Object> activityKeys = redisTemplate.keys("Activity*");
       List<KillCourse> killCourses = new ArrayList<>();
        activityKeys.forEach(activityKey->{
            List<KillCourse> values = redisTemplate.opsForHash().values(activityKey);
            killCourses.addAll(values);
        });
        return killCourses;
    }

    /**
    * @Description: 从reids中查找
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/25 16:26  
    */
    @Override
    public KillCourse onlineOne(Long killId, Long activityId) {
        return (KillCourse)redisTemplate.opsForHash().get("Activity" + activityId, killId.toString());
    }

    /**
    * @Description: 执行秒杀预创订单业务
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/25 18:28
    */
    @Override
    public String kill(KillParamDto dto) {
        log.info("进入秒杀-----------");
        Date start = new Date();
        // 需要验证不能重复秒杀，需要前端将token唯一标识传过来
        // 扣减库存前应该做各种判断 比如验证码、白名单、放盗刷等
        // 判断传入的参数不为空
        AssertUtil.isNotNull(dto.getActivityId(),"秒杀活动不能为空");
        AssertUtil.isNotNull(dto.getKillCourseId(),"秒杀课程不能为空");
        // 从redis中拿到秒杀课程
        KillCourse killCourse = onlineOne(dto.getKillCourseId(),dto.getActivityId());
        AssertUtil.isNotNull(killCourse,"该课程不存在");

        // 判断课程的状态是不是已发布
        boolean isPublish = killCourse.getPublishStatus() == KillActivity.STATES_WAIT_PUBLISH;
        AssertUtil.isTrue(isPublish,"课程不能存在");

        // 判断课程是否在秒杀时间内
        AssertUtil.isTrue(killCourse.getKilling(),"非法参数");

        // 尝试扣减信号量
        String semaphoreKey = dto.getActivityId()+ ":" + dto.getKillCourseId();
        RSemaphore semaphore = redissonClient.getSemaphore(semaphoreKey);
        // 扣减信号量，保证超卖。，最小值是0   小于0就是false
        boolean success = semaphore.tryAcquire(1);

        // 扣减不成功 就标识为抢到
        AssertUtil.isTrue(success,"手速太慢了");

        // 尝试扣减成功就生成预创订单 预创订单是指偶用来正真实现订单用的。同普通订单一样，有些还没有生成就不要写。
        Long loginId = 3L;
        PreCourseOrder preCourseOrder = new PreCourseOrder();
        preCourseOrder.setCourseId(killCourse.getCourseId());
        preCourseOrder.setUserId(loginId);
        preCourseOrder.setCoursePic(killCourse.getCoursePic());
        preCourseOrder.setTotalCount(1);
        preCourseOrder.setCourseName(killCourse.getCourseName());
        preCourseOrder.setTotalAmount(killCourse.getKillPrice());
        preCourseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        String orderNo = preCourseOrder.getOrderNo();
        // 将预创订单存入redis中，下单服务会使用
        redisTemplate.opsForValue().set(orderNo,preCourseOrder);
        long l = System.currentTimeMillis() - start.getTime();
        log.info("秒杀时间为：" + l);
        return orderNo;

        

    }

    /**
    * @Description: 客户端拿着预创订单号到结算页，
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/25 20:57
    */
    @Override
    public Course2OrderInfoVo queryPreOrder(String orderNo) {
        // 判断参数
        AssertUtil.isNotNull(orderNo,"订单号不存在");
        //从redis中拿到预定订单
        PreCourseOrder preCourseOrder = (PreCourseOrder)redisTemplate.opsForValue().get(orderNo);
        // 判空
        AssertUtil.isNotNull(preCourseOrder,"预定订单不存在");
        Course2OrderInfoVo course2OrderInfoVo = new Course2OrderInfoVo();
        course2OrderInfoVo.setTotalAmount(preCourseOrder.getTotalAmount());
        List<CourseDto> courseInfos = course2OrderInfoVo.getCourseInfos();
        // 根据预创订单的课程id拿到课程
        Course course = new Course();
        course.setPic(preCourseOrder.getCoursePic());
        course.setName(preCourseOrder.getCourseName());
        course.setId(preCourseOrder.getCourseId());
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(preCourseOrder.getTotalAmount());
        CourseDto courseDto = new CourseDto(course,courseMarket);
        courseInfos.add(courseDto);
        return course2OrderInfoVo;
    }


    private KillCourse checkKillCourse(Long courseId, Long activityId){
        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("activity_id",activityId);
        wrapper.eq("course_id",courseId);
        return selectOne(wrapper);
    }
}
